package org.glowroot.agent.plugin.hzero;

import org.glowroot.agent.plugin.api.Agent;
import org.glowroot.agent.plugin.api.OptionalThreadContext;
import org.glowroot.agent.plugin.api.TimerName;
import org.glowroot.agent.plugin.api.TraceEntry;
import org.glowroot.agent.plugin.api.weaving.*;
import org.glowroot.agent.plugin.hzero._.JobHandleMethodMeta;

import java.util.Map;

public class JobAspect {

    @Shim("org.hzero.boot.scheduler.infra.tool.SchedulerTool")
    public interface SchedulerToolShim {

        @Shim("org.hzero.boot.scheduler.api.dto.JobDataDTO getJobDataDTO()")
        JobDataDTOShim glowroot$getJobDataDTO();

    }

    @Shim("org.hzero.boot.scheduler.api.dto.JobDataDTO")
    public interface JobDataDTOShim {
        String getJobCode();
        String getJobType();
        String getJobHandler();
    }

    @Pointcut(classAnnotation = "org.hzero.boot.scheduler.infra.annotation.JobHandler",
            methodName = "execute",
            methodParameterTypes = {"java.util.Map", "org.hzero.boot.scheduler.infra.tool.SchedulerTool"},
            timerName = "Job Execute")
    public static class ExecuteAdvice {

        private static final TimerName timerName = Agent.getTimerName(ExecuteAdvice.class);

        @OnBefore
        public static TraceEntry onBefore(OptionalThreadContext context, @BindParameter final Map map,
                                          @BindParameter SchedulerToolShim schedulerTool, @BindMethodMeta JobHandleMethodMeta methodMeta) {
            JobDataDTOShim jobDataDTO = schedulerTool.glowroot$getJobDataDTO();
            String jobCode = jobDataDTO.getJobCode();
            String jobType = jobDataDTO.getJobType();
            String jobHandler = jobDataDTO.getJobHandler();
            String initClassName = methodMeta.getInitClassName();
            JobHandlerMessageSuppplier messageSuppplier = new JobHandlerMessageSuppplier(initClassName, map.toString(), jobCode, jobType, jobHandler);
            return context.startTransaction("JOB", initClassName, messageSuppplier, timerName);
        }

        @OnReturn
        public static void onReturn(@BindTraveler TraceEntry traceEntry) {
            traceEntry.end();
        }

        @OnThrow
        public static void onThrow(@BindThrowable Throwable t,
                                   @BindTraveler TraceEntry traceEntry) {
            traceEntry.endWithError(t);
        }
    }
}
